package com.acme.gprs.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

public final class GPRSAppliancesServer implements Runnable{
	public final static int GPRS_LISTENER_PORT = 1090;
	private final static int GPRS_LISTENER_QUEUE_SIZE = 1000;
	
	private List<GPRSApplianceClient> clientsCache;
	private ServerSocket listenerSocket;
	private boolean serverUp;
	private static DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
	private static Logger logger;
	
	//Singleton instance
	private static GPRSAppliancesServer instance;
	static{
		//Create the singleton instance
		try {
			instance = new GPRSAppliancesServer();
		} catch (IOException e) {
			e.printStackTrace();
		}
		//Configure the logger
		try {
			logger = Logger.getLogger(GPRSAppliancesServer.class.getCanonicalName());
			Handler fileHandler = new FileHandler("gprs_server.log");
			fileHandler.setFormatter(new SimpleFormatter());
			logger.addHandler(fileHandler);
			logger.addHandler(new ConsoleHandler());
			logger.setLevel(Level.FINEST);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private GPRSAppliancesServer() throws IOException{
		clientsCache = Collections.synchronizedList(new ArrayList<GPRSApplianceClient>());
		listenerSocket = new ServerSocket(GPRS_LISTENER_PORT,GPRS_LISTENER_QUEUE_SIZE);
		Thread serverThread = new Thread(this);
		serverThread.start();
		serverUp = true;
	}
	
	public static GPRSAppliancesServer getInstance(){
		return instance;
	}
	
	@Override
	public void run() {
		while(serverUp){
			try {
				logMessage("Waiting new connections");
				Socket newConnection = listenerSocket.accept();
				logMessage("New connection received");
				addGPRSApplianceConnection(newConnection);
				//dumpClients();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public void shutdown(){
		logMessage("Shutting down GPRSAppliancesServer...");
		if(!listenerSocket.isClosed()){
			try {
				for (GPRSApplianceClient gprsApplianceClient : clientsCache) {
					gprsApplianceClient.disconnect();
				}
				listenerSocket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		serverUp = false;
		logMessage("Shutting down GPRSAppliancesServer DONE");
	}
	
	private void addGPRSApplianceConnection(Socket connection){
		GPRSApplianceClientId applianceConnectionId = new GPRSApplianceClientId(connection.getInetAddress().getHostAddress(),connection.getPort());
		GPRSApplianceClient applianceConnection = new GPRSApplianceClient(this,applianceConnectionId,connection);
		clientsCache.add(applianceConnection);
	}
	
	void removeGPRSApplianceConnection(GPRSApplianceClient gprsAppliance){
		clientsCache.remove(gprsAppliance);
		dumpClients();
	}
	
	public GPRSApplianceClient getGPRSApplianceByImei(String imei){
		GPRSApplianceClientId id = new GPRSApplianceClientId("",0);
		id.setImei(imei);
		int index = clientsCache.indexOf(new GPRSApplianceClient(id));
		return index!=-1 ? clientsCache.get(index) : null;
	}
	
	public String statusDevice(String imei){
		String response = null;
		GPRSApplianceClient client = getGPRSApplianceByImei(imei);
		if(client!=null){
			response = "00";
		}else{
			response = "01";
			logMessage("IMEI ["+imei+"] : No connected");
		}
		return response;
	}
	
	public String sendCommand(String imei, GPRSCommand command, String[] data){
		String response = null;
		String port = null;
		GPRSApplianceClient client = getGPRSApplianceByImei(imei);
		if(client!=null){
			GPRSMessage gprsRx;
			try {
				if(command==GPRSCommand.STATUS_PORT && data != null){
					port = data[0];
					data = null;
				}
				gprsRx = client.sendCommand(command.getCode(),data);
				if(gprsRx!=null){
					switch(command){
						case OPEN_PORT:
							response = "00";
							logMessage("IMEI ["+imei+"] Open Port ["+(data[1].indexOf("0")+1)+"] : " + gprsRx.getData()[0]);
							break;
						case CLOSE_PORT:
							response = "00";
							logMessage("IMEI ["+imei+"] Close Port ["+(data[1].indexOf("1")+1)+"] : " + gprsRx.getData()[0]);
							break;
						case STATUS_PORT:
							if( port != null ){
								int status = Integer.parseInt(gprsRx.getData()[14], 16) & Integer.parseInt(port, 2);
								if( status == 0 ){
									status = 1;
								}else{
									status = 0;
								}
								response = Integer.toString(status);
								logMessage("IMEI ["+imei+"] Port Status ["+port+"] : " + response);
							}else{
								String allStatus = gprsRx.getData()[14];
								for( int i = 1; i < 7; i++){
									switch (i) {
										case 1:
											port = "00000001";
											break;
										case 2:
											port = "00000010";
											break;
										case 3:
											port = "00000100";
											break;
										case 4:
											port = "00001000";
											break;
										case 5:
										case 6:
											port = "00010000";
											break;
									}
									int status = Integer.parseInt(allStatus, 16) & Integer.parseInt(port, 2);
									if( status == 0 ){
										status = 1;
									}else{
										status = 0;
									}
									response = Integer.toString(status) + (response != null ? response : "");
								}
								logMessage("IMEI ["+imei+"] All Port Status : " + response);
							}
							break;
						case TRACK_ON_DEMAND:
							response = gprsRx.getData()[1] + "," + gprsRx.getData()[2];
							logMessage("IMEI ["+imei+"] location : " + response);
							break;
					}
				}
				
			} catch (TimeoutException timeoutException) {
				timeoutException.printStackTrace();
				response = "02";
				//response = e.getMessage();
			} catch (Exception exception) {
				exception.printStackTrace();
				response = "99";
				//response = e.getMessage();
			}
		}else{
			response = "01";
			logMessage("IMEI ["+imei+"] : No connected");
		}
		return response;
	}
	
	public void dumpClients(){
		if(clientsCache != null && !clientsCache.isEmpty()){
			logMessage("Dumping connections:");
			
			for(GPRSApplianceClient client : clientsCache){
				GPRSApplianceClientId id = client.getId();
				System.out.println("\t (DEV) (IMEI : "+ id.getImei() +"); " + id.getHostAddress()+":"+id.getPort());
			}
		}else{
			logMessage("Dumping connections: EMPTY");
		}
	}
	
	
	private static void logMessage(String msg){
		//logger.log(Level.INFO,dateFormat.format(new Date()) + " > " + msg);
		System.out.println(dateFormat.format(new Date()) + " (DEV) > " + msg);
	}
	
	public static void main(String[] args) {
		GPRSAppliancesServer.getInstance();
	}
}